using DataAnalyticsTools.Core.ChangePointDetect;
using DataAnalyticsTools.Models;
using System;
using System.Collections.Generic;
using System.Linq;

namespace DataAnalyticsTools.Core
{
    /// <summary>
    /// 趋势分析器 - 提供线性趋势分析和变化点检测功能
    /// </summary>
    public static class TrendAnalyzer
    {
        // 常量阈值定义

        /// <summary>
        /// 样本数量低于本数量，直接推荐CUSUM
        /// </summary>
        private const int MIN_SAMPLE_COUNT_FOR_OTHER_THAN_CUSUM = 6;

        /// <summary>
        /// 趋势显著性阈值，用于判断趋势方向是否有效
        /// 低于此值将被标记为无趋势(NoTrend)
        /// </summary>
        private const float SIGNIFICANCE_THRESHOLD = 0.5f;

        /// <summary>
        /// 置信度阈值，变化点置信度低于此值将被过滤掉
        /// 用于后处理阶段去除低质量的变化点检测结果
        /// </summary>
        private const float CONFIDENCE_THRESHOLD = 0.3f;

        #region 公共接口方法

        /// <summary>
        /// 计算线性趋势斜率及变化点分析
        /// </summary>
        /// <param name="data">时间序列数据</param>
        /// <param name="method">变化点检测方法</param>
        /// <param name="windowSize">窗口大小（用于CUSUM、滑动窗口、波动率方法）</param>
        /// <param name="cusumThresholdFactor">CUSUM阈值调整因子（默认1.2，建议范围0.5-3.0，值越小越敏感）</param>
        /// <param name="slidingWindowAdjustmentFactor">滑动窗口灵敏度因子（默认1.5，建议范围0.8-3.0，值越大越不敏感）</param>
        /// <param name="regressionSensitivityFactor">回归方法灵敏度因子（默认2.25，建议范围0.9-6.0，值越大越不敏感）</param>
        /// <param name="volatilitySensitivityFactor">波动率方法灵敏度因子（默认1.5，建议范围1.0-3.0，值越大越不敏感）</param>
        /// <returns>包含整体趋势、变化点和分段趋势的完整分析结果</returns>
        public static TrendInfo CalculateLinearTrendWithChangePoints(
            float[] data,
            ChangePointDetectMethod method,
            int windowSize,
            float cusumThresholdFactor = 1.2f,
            float slidingWindowAdjustmentFactor = 1.5f,
            float regressionSensitivityFactor = 2.25f,
            float volatilitySensitivityFactor = 1.5f)
        {
            // 输入验证
            if (data == null)
                throw new ArgumentNullException(nameof(data));

            int n = data.Length;
            if (n < 2)
                throw new ArgumentException("数据点至少需要2个", nameof(data));

            if (windowSize <= 0)
                throw new ArgumentException("窗口大小必须大于0", nameof(windowSize));

            if (windowSize >= n)
                throw new ArgumentException($"窗口大小({windowSize})不能大于等于数据长度({n})", nameof(windowSize));

            // 自动推荐方法
            if (method == ChangePointDetectMethod.AUTO)
            {
                method = RecommendDetectionMethod(data);
            }

            // 检测变化点
            var changePoints = ChangePointDetection(data, windowSize, method,
                cusumThresholdFactor, slidingWindowAdjustmentFactor,
                regressionSensitivityFactor, volatilitySensitivityFactor);

            // 计算整体趋势
            var overallTrend = CalculateLinearTrend(data);

            // 计算分段趋势（基于检测到的变化点）
            var segmentTrends = CalculateSegmentTrends(data, changePoints);

            // 构建完整的趋势信息
            return new TrendInfo
            {
                Method = method.ToString(),
                Slope = overallTrend.Slope,
                TrendStrength = overallTrend.TrendStrength,
                Significance = overallTrend.Significance,
                Direction = overallTrend.Direction,
                FittedValues = overallTrend.FittedValues,
                Residuals = overallTrend.Residuals,
                ChangePoints = changePoints ?? new List<ChangePoint>(),
                SegmentTrends = segmentTrends ?? new List<SegmentTrend>()
            };
        }

        /// <summary>
        /// 变化点检测
        /// </summary>
        /// <param name="values">时间序列数据</param>
        /// <param name="windowSize">窗口大小</param>
        /// <param name="method">检测方法</param>
        /// <param name="cusumThresholdFactor">CUSUM阈值调整因子</param>
        /// <param name="slidingWindowAdjustmentFactor">滑动窗口灵敏度因子</param>
        /// <param name="regressionSensitivityFactor">回归方法灵敏度因子</param>
        /// <param name="volatilitySensitivityFactor">波动率方法灵敏度因子</param>
        /// <returns>检测到的变化点列表（已排序并过滤）</returns>
        public static List<ChangePoint> ChangePointDetection(
            float[] values,
            int windowSize,
            ChangePointDetectMethod method,
            float cusumThresholdFactor = 1.2f,
            float slidingWindowAdjustmentFactor = 1.5f,
            float regressionSensitivityFactor = 2.25f,
            float volatilitySensitivityFactor = 1.5f)
        {
            if (values == null || values.Length < 2)
                return new List<ChangePoint>();

            List<ChangePoint> changePoints;

            // 根据方法调用对应的检测器
            switch (method)
            {
                case ChangePointDetectMethod.CUSUM:
                    changePoints = CUSUMDetector.Detect(values, windowSize, cusumThresholdFactor);
                    break;

                case ChangePointDetectMethod.SlidingWindow:
                    changePoints = SlidingWindowDetector.Detect(values, windowSize, slidingWindowAdjustmentFactor);
                    break;

                case ChangePointDetectMethod.Regression:
                    changePoints = RegressionDetector.Detect(values, regressionSensitivityFactor);
                    break;

                case ChangePointDetectMethod.Volatility:
                    changePoints = VolatilityDetector.Detect(values, windowSize, volatilitySensitivityFactor);
                    break;

                default:
                    changePoints = CUSUMDetector.Detect(values, windowSize, cusumThresholdFactor);
                    break;
            }

            // 后处理：过滤和去重
            changePoints = PostProcessChangePoints(changePoints, windowSize);

            return changePoints;
        }

        #endregion

        #region 私有辅助方法

        /// <summary>
        /// 根据数据类型自动推荐合适的检测方法
        /// </summary>
        private static ChangePointDetectMethod RecommendDetectionMethod(float[] data)
        {
            if (data.Length < MIN_SAMPLE_COUNT_FOR_OTHER_THAN_CUSUM)
                return ChangePointDetectMethod.CUSUM;

            // 计算数据的统计特性
            float mean = Math.Abs(data.Average());
            float stdDev = MathHelper.CalculateStdDev(data);

            // 避免除零
            if (mean < MathHelper.MIN_STD_DEV || stdDev < MathHelper.MIN_STD_DEV)
                return ChangePointDetectMethod.CUSUM;

            float cv = stdDev / mean; // 变异系数

            // 检测趋势强度和线性度
            float[] time = Enumerable.Range(0, data.Length).Select(i => (float)i).ToArray();
            var regression = SimpleLinearRegression(time, data);
            float rSquared = CalculateRSquared(data, regression.FittedValues);

            // 计算残差的标准差，用于判断数据的非线性程度
            float residualStd = MathHelper.CalculateStdDev(regression.Residuals);
            float nonLinearity = residualStd / (stdDev + MathHelper.MIN_STD_DEV);

            // 检测数据中是否有明显的跳变（通过相邻点的最大差异）
            // 区分跳变(Jump)和持续波动(Volatility)：
            // - 跳变：单个或少数几个大的相邻差异
            // - 持续波动：多个中等的相邻差异
            float maxDiff = 0f;
            float sumDiff = 0f;
            int diffCount = 0;
            for (int i = 1; i < data.Length; i++)
            {
                float diff = Math.Abs(data[i] - data[i - 1]);
                sumDiff += diff;
                diffCount++;
                if (diff > maxDiff) maxDiff = diff;
            }
            float avgDiff = sumDiff / diffCount;
            float jumpRatio = maxDiff / (stdDev + MathHelper.MIN_STD_DEV);
            float volatilityRatio = avgDiff / (stdDev + MathHelper.MIN_STD_DEV);

            // 判断是否为局部跳变：最大差异远大于平均差异
            bool hasLocalJump = (jumpRatio > 2.0f) && (maxDiff > avgDiff * 2.0f);

            // 决策逻辑（按优先级排序）

            // 1. 如果有明显局部跳变，优先使用CUSUM
            //    即使R²高，跳变数据也应该用CUSUM而非Regression
            if (hasLocalJump)
                return ChangePointDetectMethod.CUSUM;

            // 2. 如果数据有强线性趋势（R² > 0.8），使用回归方法
            //    前提是没有明显跳变
            if (rSquared > 0.8f)
                return ChangePointDetectMethod.Regression;

            // 3. 如果波动较大（变异系数 > 0.3），使用滑动窗口
            //    滑动窗口对高波动数据更稳健
            if (cv > 0.3f)
                return ChangePointDetectMethod.SlidingWindow;

            // 4. 如果数据非线性明显，使用滑动窗口
            //    适合处理非线性和复杂模式
            //    条件1: R² < 0.7 且残差占比高 (nonLinearity > 0.5)
            //    条件2: R² < 0.60 (明显的拟合度低，即使残差占比不太高也可能是非线性)
            if ((rSquared < 0.7f && nonLinearity > 0.5f) || rSquared < 0.60f)
                return ChangePointDetectMethod.SlidingWindow;

            // 5. 默认使用CUSUM（最通用的方法）
            return ChangePointDetectMethod.CUSUM;
        }

        /// <summary>
        /// 计算整体线性趋势
        /// </summary>
        private static TrendInfo CalculateLinearTrend(float[] data)
        {
            int n = data.Length;
            float[] time = Enumerable.Range(0, n).Select(i => (float)i).ToArray();

            var regression = SimpleLinearRegression(time, data);
            float rSquared = CalculateRSquared(data, regression.FittedValues);
            float significance = CalculateSignificance(regression.Slope, regression.Residuals, time, n);
            TrendDirection direction = DetermineTrendDirection(regression.Slope, significance);

            return new TrendInfo
            {
                Slope = regression.Slope,
                TrendStrength = rSquared,
                Significance = significance,
                Direction = direction,
                FittedValues = regression.FittedValues,
                Residuals = regression.Residuals
            };
        }

        /// <summary>
        /// 计算分段趋势信息
        /// </summary>
        private static List<SegmentTrend> CalculateSegmentTrends(float[] data, List<ChangePoint> changePoints)
        {
            var segments = new List<SegmentTrend>();

            if (data == null || data.Length < 2)
                return segments;

            if (changePoints == null || !changePoints.Any())
            {
                var segmentTrend = CalculateSegmentTrend(data, 0, data.Length - 1);
                segments.Add(segmentTrend);
                return segments;
            }

            int startIndex = 0;

            var sortedChangePoints = changePoints
                .Where(cp => cp.Index > 0 && cp.Index < data.Length)
                .OrderBy(cp => cp.Index)
                .Distinct()
                .ToList();

            if (!sortedChangePoints.Any())
            {
                var segmentTrend = CalculateSegmentTrend(data, 0, data.Length - 1);
                segments.Add(segmentTrend);
                return segments;
            }

            foreach (var changePoint in sortedChangePoints)
            {
                int endIndex = changePoint.Index - 1;

                if (endIndex >= startIndex && endIndex - startIndex + 1 >= 2)
                {
                    var segmentData = new float[endIndex - startIndex + 1];
                    Array.Copy(data, startIndex, segmentData, 0, segmentData.Length);
                    var segmentTrend = CalculateSegmentTrend(segmentData, startIndex, endIndex);
                    segments.Add(segmentTrend);
                }

                startIndex = changePoint.Index;
            }

            if (startIndex < data.Length && data.Length - startIndex >= 2)
            {
                var lastSegmentData = new float[data.Length - startIndex];
                Array.Copy(data, startIndex, lastSegmentData, 0, lastSegmentData.Length);
                var lastSegmentTrend = CalculateSegmentTrend(lastSegmentData, startIndex, data.Length - 1);
                segments.Add(lastSegmentTrend);
            }

            return segments;
        }

        /// <summary>
        /// 计算单个分段的趋势信息
        /// </summary>
        private static SegmentTrend CalculateSegmentTrend(float[] segmentData, int startIndex, int endIndex)
        {
            int n = segmentData.Length;
            float[] time = Enumerable.Range(0, n).Select(i => (float)i).ToArray();

            var regression = SimpleLinearRegression(time, segmentData);
            float rSquared = CalculateRSquared(segmentData, regression.FittedValues);
            float significance = CalculateSignificance(regression.Slope, regression.Residuals, time, n);
            TrendDirection direction = DetermineTrendDirection(regression.Slope, significance);

            return new SegmentTrend
            {
                StartIndex = startIndex,
                EndIndex = endIndex,
                Slope = regression.Slope,
                TrendStrength = rSquared,
                Direction = direction
            };
        }

        /// <summary>
        /// 后处理变化点：过滤低置信度点并合并相近的点
        /// </summary>
        private static List<ChangePoint> PostProcessChangePoints(List<ChangePoint> changePoints, int minDistance)
        {
            if (changePoints == null || !changePoints.Any())
                return new List<ChangePoint>();

            // 1. 过滤低置信度的变化点
            var filtered = changePoints
                .Where(cp => cp.Confidence >= CONFIDENCE_THRESHOLD)
                .OrderBy(cp => cp.Index)
                .ToList();

            if (!filtered.Any())
                return new List<ChangePoint>();

            // 2. 合并相近的变化点（距离小于minDistance的点）
            var result = new List<ChangePoint> { filtered[0] };

            for (int i = 1; i < filtered.Count; i++)
            {
                var current = filtered[i];

                if (current.Index - result.Last().Index >= minDistance)
                {
                    result.Add(current);
                }
                else
                {
                    // 距离太近，保留置信度更高的变化点
                    if (current.Confidence > result.Last().Confidence)
                    {
                        result[result.Count - 1] = current;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// 执行简单线性回归分析
        /// </summary>
        private static (float Slope, float Intercept, float[] FittedValues, float[] Residuals)
            SimpleLinearRegression(float[] x, float[] y)
        {
            int n = x.Length;
            float xMean = x.Average();
            float yMean = y.Average();

            float numerator = 0f;
            float denominator = 0f;

            for (int i = 0; i < n; i++)
            {
                numerator += (x[i] - xMean) * (y[i] - yMean);
                denominator += (x[i] - xMean) * (x[i] - xMean);
            }

            float slope = denominator == 0 ? 0 : numerator / denominator;
            float intercept = yMean - slope * xMean;

            float[] fittedValues = new float[n];
            float[] residuals = new float[n];

            for (int i = 0; i < n; i++)
            {
                fittedValues[i] = slope * x[i] + intercept;
                residuals[i] = y[i] - fittedValues[i];
            }

            return (slope, intercept, fittedValues, residuals);
        }

        /// <summary>
        /// 计算决定系数 R²
        /// </summary>
        private static float CalculateRSquared(float[] actual, float[] predicted)
        {
            float actualMean = actual.Average();
            float ssTotal = actual.Sum(y => (y - actualMean) * (y - actualMean));
            float ssResidual = actual.Zip(predicted, (a, p) => (a - p) * (a - p)).Sum();

            if (ssTotal == 0) return 1f;

            float rSquared = 1f - ssResidual / ssTotal;
            return Math.Max(0f, Math.Min(1f, rSquared));
        }

        /// <summary>
        /// 计算趋势显著性（使用标准的t检验方法）
        /// </summary>
        private static float CalculateSignificance(float slope, float[] residuals, float[] x, int n)
        {
            if (n < 3) return 0f;
            if (Math.Abs(slope) < MathHelper.MIN_STD_DEV) return 0f;

            // 1. 计算残差标准误差
            float ssResidual = residuals.Sum(r => r * r);
            float mse = ssResidual / (n - 2);
            float seResidual = (float)Math.Sqrt(mse);

            if (seResidual < MathHelper.MIN_STD_DEV) return 1.0f;

            // 2. 计算x的平方和
            float xMean = x.Average();
            float sxx = x.Sum(xi => (xi - xMean) * (xi - xMean));

            if (sxx < MathHelper.MIN_STD_DEV) return 0f;

            // 3. 计算斜率的标准误差
            float seSlope = seResidual / (float)Math.Sqrt(sxx);

            // 4. 计算t统计量
            float tStat = Math.Abs(slope / seSlope);

            // 5. 将t统计量转换为显著性值
            int df = n - 2;
            float tNormalized = tStat * tStat / df;
            float significance = 1.0f - (float)Math.Exp(-tNormalized);

            return Math.Max(0f, Math.Min(1f, significance));
        }

        /// <summary>
        /// 确定趋势方向
        /// </summary>
        private static TrendDirection DetermineTrendDirection(float slope, float significance)
        {
            if (significance < SIGNIFICANCE_THRESHOLD)
                return TrendDirection.NoTrend;

            return slope > 0 ? TrendDirection.Increasing : TrendDirection.Decreasing;
        }

        #endregion
    }
}
